#include "misc_tools.hpp"

#include <cstdio>
#include <cstdlib>

static bool PlatformActiveWindow (long pid);

#if _WIN32

#include <Windows.h>
#include <utility>
#include <cstring>

bool PlatformActiveWindow (long pid)
{
    // 查找窗口
    auto params = std::make_pair (static_cast<HWND> (nullptr), static_cast<DWORD> (pid));
    BOOL result = EnumWindows ([](HWND hwnd, LPARAM lparam)-> BOOL {
        const auto param = reinterpret_cast<std::pair<HWND, DWORD> *> (lparam);
        DWORD      pid   = 0;
        GetWindowThreadProcessId (hwnd, &pid);
        char window_class[256]{};
        GetWindowTextA(hwnd, window_class, sizeof(window_class)-1);
        if (pid == param->second && std::strncmp(window_class, "Diary Tool C++", 14) == 0)
        {
            // FOUND! STOP ENUMERATING
            param->first = hwnd;
            SetLastError (0x123456);
            return FALSE;
        }
        // CONTINUE ENUMERATING
        return TRUE;
    }, reinterpret_cast<LPARAM> (&params));

    if (result == FALSE && GetLastError () == 0x123456 && params.first != nullptr)
    {
        ::ShowWindow(params.first, SW_RESTORE);
        ::SetForegroundWindow (params.first);
        return true;
    }
    // abort();
    return false;
}

#elif __unix__

#if X11_OK

#include <X11/Xlib.h>
#include <X11/Xatom.h>
#include <list>
#include <cstring>

class WindowsMatchingPid
{
public:
    WindowsMatchingPid(Display *display, Window wRoot, unsigned long pid)
            : _pid(pid)
            , _display(display)
    {
        // Get the PID property atom.
        _atomPID = XInternAtom(display, "_NET_WM_PID", True);
        if(_atomPID != None)
        {
            search(wRoot);
        }
    }

    [[nodiscard]] const std::list<Window> &result() const { return _result; }

private:
    unsigned long  _pid;
    Atom           _atomPID;
    Display       *_display;
    std::list<Window>   _result;

    void search(Window w)
    {
        // Get the PID for the current Window.
        Atom           type;
        int            format;
        unsigned long  nItems;
        unsigned long  bytesAfter;
        unsigned char *propPID = 0;
        if(Success == XGetWindowProperty(_display, w, _atomPID, 0, 1, False, XA_CARDINAL,
                                         &type, &format, &nItems, &bytesAfter, &propPID))
        {
            if(propPID != 0)
            {
                // If the PID matches, add this window to the result set.
                if(_pid == *((unsigned long *)propPID))
                    _result.push_back(w);

                XFree(propPID);
            }
        }

        // Recurse into child windows.
        Window    wRoot;
        Window    wParent;
        Window   *wChild;
        unsigned  nChildren;
        if(0 != XQueryTree(_display, w, &wRoot, &wParent, &wChild, &nChildren))
        {
            for(unsigned i = 0; i < nChildren; i++)
                search(wChild[i]);
        }
    }
};


bool PlatformActiveWindow (long pid)
{
    if (pid == 0)
        return false;

    // find window
    Display *display = XOpenDisplay(nullptr);
    WindowsMatchingPid match(display, XDefaultRootWindow(display), pid);
    const auto &result = match.result();
    bool ok = false;
    for (const auto &x: result) {
        Window win = x;
        char *title = nullptr;
        if (XFetchName(display, win, &title) != 0) {
            if  (strlen(title) > 0) {

                XEvent  event{0};
                event.xclient.type = ClientMessage;
                event.xclient.serial = 0;
                event.xclient.send_event = True;
                event.xclient.message_type = XInternAtom(display, "_NET_ACTIVE_WINDOW", False);
                event.xclient.window = win;
                event.xclient.format = 32;
                XSendEvent(display, DefaultRootWindow(display), False, SubstructureRedirectMask | SubstructureNotifyMask, &event);

                XMapRaised(display, win);
                XFree(title);
                
                ok = true;
            }
            else
                XFree(title);
        }
    }
    XCloseDisplay(display);
    return ok;
}

#else

#warning "X11 not found, some function not aviable!"

bool PlatformActiveWindow (long pid)
{
    fprintf(stderr, "currently only x11 platform support!!\n");
    return false;
}

#endif

#else
#error "Code not ported yet!"
#endif

void show_proccess_main_window (long pid)
{
    const bool result = PlatformActiveWindow (pid);
    if (!result)
        fprintf(stderr, "Failed to show proccess window!!\n");
}
